home *** CD-ROM | disk | FTP | other *** search
/ Super PC 34 / Super PC 34 (Shareware).iso / spc / UTIL / DJGPP2 / V2 / DJLSR200.ZIP / src / libc / ansi / stdio / rename.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-01-24  |  13.1 KB  |  469 lines

  1. /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
  2. /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
  3. /* ------------------------- rename() for DJGPP -------------------------- */
  4.  
  5. /*
  6.  *  An implementation of rename() which can move both files AND
  7.  *  directories on the same filesystem (in the DOS world this means
  8.  *  the same logical drive).  Most cases are simply passed to the
  9.  *  DOS Int 21h/AH=56h function.  Special treatment is required for
  10.  *  renaming (moving) directories which don't share their parent
  11.  *  directory, because DOS won't allow this.  This is called ``Prune
  12.  *  and graft'' operation.  Most of the code below handles this
  13.  *  special case.  It recursively creates subdirectories at the
  14.  *  target path, moves regular files there, then deletes the (empty)
  15.  *  directories at the source.
  16.  *
  17.  *  An alternative (and much faster) implementation would be to access
  18.  *  the parent directories of the source and the target at the disk
  19.  *  sector level and rewrite them with BIOS calls.  However, this won't
  20.  *  work for networked drives and will leave the file system in an
  21.  *  inconsistent state, should the machine crash before the operation
  22.  *  is completed.  (A hybrid approach which uses the fast method when
  23.  *  possible and the slow one otherwise, is left as an exercise for the
  24.  *  ambitious readers. ;-)
  25.  */
  26.  
  27. #include <libc/stubs.h>
  28. #include <libc/bss.h>
  29. #include <stdio.h>
  30. #include <string.h>
  31. #include <stdlib.h>
  32. #include <ctype.h>
  33. #include <errno.h>
  34. #include <io.h>
  35. #include <sys/stat.h>
  36. #include <dir.h>
  37.  
  38. /* -------------------------------------------------------
  39.        Internal variables and static helper functions.
  40.    ------------------------------------------------------- */
  41.  
  42. /* A stack of directories queued for deletion as soon as they are
  43.    emptied.  Implemented as an array of structures; each element
  44.    contains a ptr into a char array (the pool) where the names of
  45.    the directories are stored, and the length of the pathname of
  46.    that directory.  */
  47.  
  48. typedef struct _stacked_dir {
  49.   char * dirname;
  50.   int    dirlen;
  51. } Stacked_Dir;
  52.  
  53. static char        * dirnames_pool;    /* the pool of names */
  54. static int           pool_size;        /* size of the pool */
  55. static Stacked_Dir * dirstack;         /* the stack itself */
  56. static Stacked_Dir * stack_top;        /* top of stack */
  57. static int           stack_size;       /* current stack size */
  58.  
  59. static char          target[FILENAME_MAX];
  60. static char          source[FILENAME_MAX];
  61. static int           last;
  62.  
  63. static int           rename_count = -1;
  64.  
  65. /*
  66. ** Initialize the stack.  Make sure the storage is allocated.
  67. */
  68. static int
  69. init_stack(void)
  70. {
  71.   /* Init static variables at program start, or if we have been
  72.      restarted (emacs).  */
  73.   if (__bss_count != rename_count)
  74.     {
  75.       rename_count = __bss_count;
  76.       dirstack = (Stacked_Dir *)0;
  77.       pool_size = 1024;
  78.       stack_size = 32;
  79.     }
  80.  
  81.   if (dirstack == 0)
  82.     {
  83.       /* Initialize storage.  */
  84.       dirnames_pool = (char *)malloc(pool_size);
  85.       if (dirnames_pool == 0)
  86.         {
  87.           errno = ENOMEM;
  88.           return 0;
  89.         }
  90.  
  91.       dirstack = (Stacked_Dir *)malloc(stack_size * sizeof(Stacked_Dir));
  92.       if (dirstack == 0)
  93.         {
  94.           errno = ENOMEM;
  95.           return 0;
  96.         }
  97.     }
  98.  
  99.   /* Forget the previous contents of the stack.  */
  100.   stack_top = dirstack;
  101.   stack_top->dirname = dirnames_pool;
  102.   stack_top->dirlen  = 0;
  103.  
  104.   return 1;
  105. }
  106.  
  107. /*
  108. ** Push a directory onto the stack, return non-zero in
  109. ** case of success.
  110. */
  111.  
  112. static int
  113. push_dir(const char *dir)
  114. {
  115.   int dspace = strlen(dir) + 1;
  116.   char * pool_end;      /* where unised space in pool begins */
  117.  
  118.   pool_end = stack_top->dirname + stack_top->dirlen;
  119.  
  120.   /* Ensure we have enough space in the name pool for this directory.  */
  121.   if (pool_end + dspace >= dirnames_pool + pool_size)
  122.     {
  123.       char * temp;
  124.  
  125.       /* Make its size doubled, plus a space for this directory.  */
  126.       pool_size += dspace + pool_size;
  127.       temp = (char *)realloc(dirnames_pool, pool_size);
  128.       if (temp == 0)
  129.         {
  130.           errno = ENOMEM;
  131.           return 0;
  132.         }
  133.       pool_end += temp - dirnames_pool;
  134.       dirnames_pool = temp;
  135.     }
  136.  
  137.   /* Bump stack top and check for stack overflow.  */
  138.   if (++stack_top - dirstack >= stack_size)
  139.     {
  140.       /* Not enough storage--reallocate.  */
  141.       Stacked_Dir * temp;
  142.  
  143.       stack_size *= 2;
  144.       temp = (Stacked_Dir *)realloc(dirstack,
  145.                                     stack_size * sizeof(Stacked_Dir));
  146.       if (temp == 0)
  147.         {
  148.           errno = ENOMEM;
  149.           return 0;
  150.         }
  151.       stack_top += temp - dirstack;
  152.       dirstack = temp;
  153.     }
  154.  
  155.   /* Now push the directory onto the stack.  */
  156.   stack_top->dirname = strcpy(pool_end, dir);
  157.   stack_top->dirlen = dspace;
  158.  
  159.   return 1;
  160. }
  161.  
  162. /*
  163. ** Pop a directory off the stack, return its name.
  164. */
  165.  
  166. static char *
  167. pop_dir(void)
  168. {
  169.   char * retval;
  170.  
  171.   if (stack_top < dirstack || stack_top->dirlen == 0) /* empty stack */
  172.     return (char *)0;
  173.  
  174.   retval = stack_top->dirname;
  175.   stack_top--;
  176.   return retval;
  177. }
  178.  
  179. /*
  180. ** See if DIR1 is a parent of DIR2, return 1 if it is.
  181. ** Note that this is in no way a general solution: it only
  182. ** works in the context of mover() operation which always
  183. ** gets filenames rooted at the same directory.  Otherwise,
  184. ** you must pass a fully-qualified pathnames for this to
  185. ** work.
  186. */
  187.  
  188. static int
  189. is_parent(const char *dir1, const char *dir2)
  190. {
  191.   return dir1 != 0 && dir2 != 0 && *dir1 != 0 && strstr(dir2, dir1) == dir2;
  192. }
  193.  
  194. /*
  195. ** Main workhorse.  This will be passed to __file_tree_walk()
  196. ** to do the actual job of moving a subtree to another branch
  197. ** of the filesystem hierarchy.
  198. */
  199.  
  200. static int
  201. mover(const char *from, const struct ffblk *ff)
  202. {
  203.   char  to[FILENAME_MAX];
  204.  
  205.   /* Did we just finish to empty a directory?  */
  206.   if (!is_parent(stack_top->dirname, from))
  207.     {
  208.       /* Remove an empty directory and pop it from stack.  */
  209.       if (remove(pop_dir()))
  210.         return -1;
  211.     }
  212.  
  213.   strcpy(to, target);
  214.   strcat(to, from + last);
  215.  
  216.   if (ff->ff_attrib & 0x10)
  217.     {
  218.       /* A directory -- create its namesake and push it onto stack.   */
  219.       if (mkdir(to, ff->ff_attrib & 0xffe7) == -1 ||
  220.           push_dir(from) == 0)
  221.         return -1;
  222.     }
  223.   else  /* a file -- move it */
  224.     {
  225.       if (_rename(from, to) == -1)
  226.         return -1;
  227.     }
  228.  
  229.   return 0;
  230. }
  231.  
  232.  
  233. /* -------------------------------------------------------
  234.        Main entry point and the only exported function
  235.    ------------------------------------------------------- */
  236.  
  237. int
  238. rename(const char *old, const char *new)
  239. {
  240.   int status;
  241.   char *p;
  242.   int source_attr, target_attr;
  243.   int old_dev, new_dev;
  244.   int e = errno;
  245.   int simple_should_do = 0; /* _rename() enough? */
  246.   int target_exists = 0;
  247.  
  248.   /* Much of the following quite tedious administrivia is necessary
  249.      to return a meaningful code in errno.  Otherwise, for most of
  250.      the calamities DOS will feed us the ubiquitous EACCES which
  251.      doesn't tell us much.  */
  252.  
  253.   /* There are several conditions which we must check upfront,
  254.      to ensure NEW isn't deleted unless rename() is to succeed.  */
  255.  
  256.   /* Fail with EFAULT if one of OLD or NEW is a NULL ptr.  */
  257.   if (old == 0 || new == 0)
  258.     {
  259.       errno = EFAULT;
  260.       return -1;
  261.     }
  262.  
  263.   /* Fail with ENAMETOOLONG if either OLD or NEW are too long.  This is
  264.      explicitly checked so that DOS filename truncation won't fool us.  */
  265.   if (strlen(old) > FILENAME_MAX || strlen(new) > FILENAME_MAX)
  266.     {
  267.       errno = ENAMETOOLONG;
  268.       return -1;
  269.     }
  270.  
  271.   /* Fail with ENOENT if OLD doesn't exist or is otherwise
  272.      inaccessible.  */
  273.   if ((source_attr = _chmod(old, 0)) == -1)
  274.     return -1;      /* errno set by _chmod() */
  275.  
  276.   /* Fail with EXDEV, if old and new aren't on the same device.  */
  277.   if (old[1] == ':')
  278.     old_dev = toupper(old[0]) - 'A';
  279.   else
  280.     old_dev = getdisk();
  281.   if (new[1] == ':')
  282.     new_dev = toupper(new[0]) - 'A';
  283.   else
  284.     new_dev = getdisk();
  285.   if (old_dev != new_dev)
  286.     {
  287.       errno = EXDEV;
  288.       return -1;
  289.     }
  290.  
  291.   /* Refuse to rename `.' or `..' or `d:.' or `d:..'  */
  292.   if ((old[0] == '.' && (old[1] == '\0' || (old[1] == '.' && old[2] == '\0'))) ||
  293.       (old[1] == ':' && old[2] == '.' && (old[3] == '\0' || (old[3] == '.' && old[4] == '\0'))))
  294.     {
  295.       errno = EINVAL;
  296.       return -1;
  297.     }
  298.  
  299.   /* Some problems can only happen if NEW already exists.  */
  300.   errno = 0;
  301.   target_attr = _chmod(new, 0);
  302.   if (errno != ENOENT)
  303.     {
  304.       int old_is_dir = (source_attr & 0x10) == 0x10;
  305.       int new_is_dir = (target_attr & 0x10) == 0x10;
  306.  
  307.       target_exists = 1;
  308.  
  309.       /* Fail if OLD is a directory while NEW isn't, or vice versa.  */
  310.       if (old_is_dir && !new_is_dir)
  311.         {
  312.           errno = ENOTDIR;
  313.           return -1;
  314.         }
  315.       else if (new_is_dir && !old_is_dir)
  316.         {
  317.           errno = EISDIR;
  318.           return -1;
  319.         }
  320.       else if (old_is_dir && new_is_dir)
  321.         {
  322.           char new_true[FILENAME_MAX], old_true[FILENAME_MAX];
  323.  
  324.           /* Fail if both OLD and NEW are directories and
  325.              OLD is parent of NEW.  */
  326.           errno = 0;
  327.           if (is_parent(_truename(old, old_true), _truename(new, new_true)))
  328.             {
  329.               errno = EINVAL;
  330.               return -1;
  331.             }
  332.           else if (errno)
  333.             return -1;
  334.           else
  335.             {
  336.               /* See if these two directories share a common parent.  If
  337.                  they do, then _rename() will do the job for us.  */
  338.  
  339.               char *s = strrchr(old_true, '\\'), *t = strrchr(new_true, '\\');
  340.  
  341.               *s = '\0'; /* truncate at end of parent directory name */
  342.               *t = '\0';
  343.               if (strcmp(old_true, new_true) == 0)
  344.                 simple_should_do = 1;
  345.             }
  346.         }
  347.       else
  348.         {
  349.           /* They both are files, _rename() must be enough.  */
  350.           simple_should_do = 1;
  351.         }
  352.     }
  353.  
  354.   /* On to some REAL work for a change.  Let DOS do the simple job:
  355.      moving a regular file, or renaming a directory.  */
  356.   if ((status = _rename(old, new)) == 0)
  357.     {
  358.       errno = e;    /* restore errno we inherited */
  359.       return 0;
  360.     }
  361.   else if (simple_should_do)
  362.     /* If _rename() fails and we KNOW it shouldn't, give up
  363.        and return -1 with whatever errno we have.  */
  364.     return -1;
  365.   else if (errno == EACCES && target_exists && (target_attr & 0x10) &&
  366.            _chmod(new, 0) != -1)
  367.     {
  368.       /* If target still exists (i.e., it wasn't removed inside
  369.          _rename()) and is a directory, assume it's not empty.
  370.          (We could verify that with findfirst()/findnext(), but that
  371.          would be too expensive.  Besides, what other reason could DOS
  372.          possibly have for not letting us remove a directory?? ;-)  */
  373.  
  374.       errno = ENOTEMPTY;
  375.       return -1;
  376.     }
  377.   else
  378.     {
  379.       /* We are in for some dirty work...  Sigh...
  380.          Recursively traverse the directory tree rooted at OLD,
  381.          moving regular files and creating subdirectories at NEW.  */
  382.  
  383.       int retval;
  384.  
  385.       strcpy(source, old);
  386.       last = strlen(source) - 1;
  387.  
  388.       strcpy(target, new);
  389.       if (strchr(target, '\\'))
  390.         for (p = target; *p; p++)
  391.           {
  392.             if (*p == '\\')
  393.               *p = '/';
  394.           }
  395.       if (source[last] == '/' || source[last] == '\\')
  396.         source[last] = '\0';
  397.       else
  398.         ++last;
  399.  
  400.       /* Create NEW and push it onto the stack.  */
  401.       if (mkdir(new, source_attr & 0xffe7) == -1 ||
  402.           init_stack() == 0 || push_dir(old) == 0)
  403.         return -1;
  404.  
  405.       /* Process all of its siblings.  */
  406.       retval = __file_tree_walk(source, mover);
  407.  
  408.       if (retval)
  409.         {
  410.  
  411.           /* If anything went wrong, remove NEW in a desperate attempt
  412.              to leave everything as we found it.  */
  413.           int savederr = errno;
  414.  
  415.           remove(target);
  416.           errno = savederr;
  417.         }
  418.       else
  419.         {
  420.           char *p2;
  421.  
  422.           /* Remove any (empty) directories that remain on the stack.
  423.              This is required because we only detect that a subdirectory
  424.              can be removed when we see the next entry in its parent dir.
  425.              Subdirectories which are the LAST (or the only) entries in
  426.              their parent directories, won't be removed.  */
  427.           while ((p2 = pop_dir()) != 0)
  428.             {
  429.               errno = e;
  430.               if (remove(p2) != 0)
  431.                 {
  432.                   int se = errno;
  433.  
  434.                   remove(target);
  435.                   errno = se;
  436.                   return -1;
  437.                 }
  438.             }
  439.         }
  440.  
  441.       return retval;
  442.     }
  443. }
  444.  
  445. #ifdef  TEST
  446.  
  447. #include <stdlib.h>
  448.  
  449. int
  450. main(int argc, char *argv[])
  451. {
  452.   if (argc > 2)
  453.     {
  454.       char msg[80];
  455.  
  456.       sprintf(msg, "movdir: %d", rename(argv[1], argv[2]));
  457.       if (errno)
  458.         perror(msg);
  459.       else
  460.         puts(msg);
  461.     }
  462.   else
  463.     printf("Usage: %s from to\n", argv[0]);
  464.  
  465.   return 0;
  466. }
  467.  
  468. #endif
  469.